package com.tfswx.fljdmxyy.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import com.newbie.core.exception.BusinessException;
import com.tfswx.fljdmxyy.constant.SystemConstant;
import com.tfswx.fljdmxyy.dao.WjXtWjMapper;
import com.tfswx.fljdmxyy.dto.wjgl.*;
import com.tfswx.fljdmxyy.enums.BusinessCodeEnum;
import com.tfswx.fljdmxyy.enums.WjlxEnum;
import com.tfswx.fljdmxyy.model.WjXtWj;
import com.tfswx.fljdmxyy.query.wjgl.WjlbQueryResult;
import com.tfswx.fljdmxyy.service.WjglService;
import com.tfswx.fljdmxyy.utils.ConverterUtils;
import com.tfswx.fljdmxyy.utils.UuidUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;

/**
 * 文件管理实现类
 *
 * @Author 陈文佳
 * @Date 2023/03/24
 */
@Service
@Slf4j
public class WjglServiceImpl implements WjglService {

    @Resource
    private WjXtWjMapper wjXtWjMapper;

    /**
     * 文件存储路径
     */
    @Value("${rootDir}")
    private String wjcclj;

    @PostConstruct
    public void init() {
        File file = new File(wjcclj);
        if (!file.exists() && !file.mkdirs()) {
            throw new BusinessException(BusinessCodeEnum.CJWJJSB);
        }
    }

    @Override
    public void download(WjDownloadInputDTO dto, HttpServletResponse response) {
        // 1.通过文件编码获取文件地址
        WjXtWj wj = ConverterUtils.convert(dto, WjXtWj.class);
        wj = wjXtWjMapper.getWjXtWj(wj);
        if (ObjectUtils.isEmpty(wj)) {
            throw new BusinessException(BusinessCodeEnum.HQSJSB);
        }

        // 2.通过文件地址下载文件
        String jdlj = new StringJoiner(File.separator)
                .add(System.getProperty("user.dir"))
                .add(wj.getWjlj())
                .add(wj.getWjcfmc())
                .toString();
        try (InputStream inputStream = Files.newInputStream(Paths.get(jdlj));
             OutputStream outputStream = response.getOutputStream()) {
            String filename = URLEncoder.encode(wj.getWjmc(), "UTF-8");
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                    String.format("attachment;filename=\"%s\";filename*=utf-8''%s", filename, filename));
            IoUtil.copy(inputStream, outputStream);
        } catch (Exception e) {
            log.error("获取文件失败: {}", e.getMessage());
            throw new BusinessException(BusinessCodeEnum.XZWJSB);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean upload(WjUploadInputDTO dto) {
        // 1.保存文件信息至数据库
        MultipartFile file = dto.getFile();
        WjXtWj wj = ConverterUtils.convert(dto, WjXtWj.class);
        wj.setWjbm(UuidUtils.uuid32());
        wj.setWjdx(file.getSize());
        wj.setWjmc(file.getOriginalFilename());
        wj.setWjcfmc(wj.getWjbm());
        String fbm = dto.getFbm();
        String wjlj = StringUtils.isBlank(fbm) ? wjcclj : fbm;
        wj.setWjlj(wjlj);
        Date now = new Date();
        wj.setCjsj(now);
        wj.setZhxgsj(now);
        wj.setSfsc(SystemConstant.N);
        int insertNum = wjXtWjMapper.insertWjXtWj(wj);
        if (insertNum <= 0) {
            throw new BusinessException(BusinessCodeEnum.BCWJSB);
        }

        // 2.上传文件到服务器
        File mbfile = new File(wjlj + File.separator + wj.getWjbm());
        try {
            FileUtil.writeFromStream(file.getInputStream(), mbfile);
        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage());
            throw new BusinessException(BusinessCodeEnum.BCWJSB);
        }
        return Boolean.TRUE;
    }

    @Override
    public List<WjlbQueryResult> queryWjlb() {
        // 1.构造顶级文件夹对象
        File file = new File(wjcclj);
        WjlbQueryResult fwj = new WjlbQueryResult();
        fwj.setFbm("");
        fwj.setBm(wjcclj);
        fwj.setMc(file.getName());
        fwj.setLx(WjlxEnum.WJJ.getCode());
        fwj.setZhxgsj(FileUtil.lastModifiedTime(file));
        List<WjlbQueryResult> list = Lists.newArrayList();
        list.add(fwj);

        // 2.查询数据库中文件信息
        List<WjXtWj> wjList = wjXtWjMapper.queryWjXtWj(new WjXtWj());

        // 3.递归查询下级文件对象
        listWj(file, fwj, list, wjList);

        // 4.按文件类型和最后修改时间排序
        return list.stream().sorted(Comparator.comparing(WjlbQueryResult::getLx)
                .thenComparing(WjlbQueryResult::getZhxgsj, Comparator.reverseOrder()))
                .collect(Collectors.toList());
    }

    @Override
    public Boolean addWjj(WjjAddInputDTO dto) {
        String wjlj = StringUtils.isBlank(dto.getFbm()) ? wjcclj : dto.getFbm();
        File file = new File(wjlj + File.separator + dto.getWjmc());
        if (!file.exists() && !file.mkdirs()) {
            throw new BusinessException(BusinessCodeEnum.CJWJJSB);
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateWj(WjUpdateInputDTO dto) {
        Integer lx = dto.getLx();
        String bm = dto.getBm();
        String wjmc = dto.getWjmc();
        if (WjlxEnum.WJJ.getCode().equals(lx)) {
            // 1.如果是修改文件夹，需要修改该文件夹下所有文件信息
            File file = new File(bm);
            List<String> wjbmList = FileUtil.loopFiles(file).stream().filter(File::isFile).map(File::getName)
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(wjbmList)) {
                String rootPath = bm.substring(0, bm.lastIndexOf("\\") + 1);
                String newbm = rootPath + wjmc;
                wjbmList.forEach(wjbm -> {
                    // 1.1修改每个文件的路径，将原本路径替换为修改后的路径
                    WjXtWj wj = new WjXtWj();
                    wj.setWjbm(wjbm);
                    wj.setZhxgsj(new Date());
                    WjXtWj updateWj = wjXtWjMapper.getWjXtWj(wj);
                    String oldPath = updateWj.getWjlj();
                    String substring = oldPath.substring(bm.length());
                    wj.setWjlj(newbm + substring);
                    int updateNum = wjXtWjMapper.updateWjXtWj(wj);
                    if (updateNum <= 0) {
                        throw new BusinessException(BusinessCodeEnum.XGSJSB);
                    }
                });
            }
            // 1.2修改完所有文件信息后，重命名文件夹
            FileUtil.rename(file, wjmc, false);
        } else if (WjlxEnum.WJ.getCode().equals(lx)) {
            // 2.如果是修改文件，则仅需要文件信息
            WjXtWj wj = new WjXtWj();
            wj.setWjbm(bm);
            wj.setWjmc(wjmc);
            wj.setZhxgsj(new Date());
            wjXtWjMapper.updateWjXtWj(wj);
        } else {
            throw new BusinessException(BusinessCodeEnum.XGSJSB);
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWj(WjDeleteInputDTO dto) {
        String bm = dto.getBm();
        File file = new File(bm);
        Date now = new Date();
        if (WjlxEnum.WJ.getCode().equals(dto.getLx())) {
            // 1.如果是文件，需要删除数据库中的文件信息
            WjXtWj wj = new WjXtWj();
            wj.setWjbm(bm);
            wj = wjXtWjMapper.getWjXtWj(wj);
            wj.setZhxgsj(now);
            int deleteNum = wjXtWjMapper.deleteWjXtWj(wj);
            if (deleteNum <= 0) {
                throw new BusinessException(BusinessCodeEnum.SCSJSB);
            }

            // 1.1如果是文件则file地址为文件路径加文件存放名称
            bm = wj.getWjlj() + File.separator + wj.getWjcfmc();
            file = new File(bm);
        } else if (WjlxEnum.WJJ.getCode().equals(dto.getLx())) {
            // 2.如果是文件夹，则需要从数据库删除该文件夹下所有文件信息
            List<String> wjbmList = FileUtil.loopFiles(file).stream().filter(File::isFile).map(File::getName)
                    .collect(Collectors.toList());
            wjbmList.forEach(wjbm -> {
                WjXtWj wj = new WjXtWj();
                wj.setWjbm(wjbm);
                wj.setZhxgsj(now);
                int deleteNum = wjXtWjMapper.deleteWjXtWj(wj);
                if (deleteNum <= 0) {
                    throw new BusinessException(BusinessCodeEnum.SCSJSB);
                }
            });
        } else {
            throw new BusinessException(BusinessCodeEnum.SCSJSB);
        }

        // 3.如果删除失败则回滚数据库操作
        if (!FileUtil.del(file)) {
            throw new BusinessException(BusinessCodeEnum.SCSJSB);
        }
        return Boolean.TRUE;
    }

    /**
     * 递归查询文件树列表
     *
     * @param fwj
     * @param fwjDto
     * @param list
     * @param wjList
     */
    private void listWj(File fwj, WjlbQueryResult fwjDto, List<WjlbQueryResult> list, List<WjXtWj> wjList) {
        if (FileUtil.isEmpty(fwj) || fwj.listFiles() == null) {
            return;
        }
        for (File file : fwj.listFiles()) {
            WjlbQueryResult zwjDto = new WjlbQueryResult();
            String fbm = fwjDto.getBm();
            zwjDto.setFbm(fbm);
            zwjDto.setZhxgsj(FileUtil.lastModifiedTime(file));
            if (file.isDirectory()) {
                // 1.如果是文件夹，则其编码为自身路径
                String wjlj = StringUtils.isBlank(fbm) ? file.getName() : fbm + File.separator + file.getName();
                zwjDto.setBm(wjlj);
                zwjDto.setMc(file.getName());
                zwjDto.setLx(WjlxEnum.WJJ.getCode());
                list.add(zwjDto);
                listWj(file, zwjDto, list, wjList);
            } else {
                // 2.如果是文件，则其编码为文件编码
                WjXtWj wj = wjList.stream().filter(item -> StringUtils.equals(item.getWjbm(), file.getName()))
                        .findFirst().orElse(null);
                if (ObjectUtils.isNotEmpty(wj)) {
                    zwjDto.setBm(wj.getWjbm());
                    zwjDto.setMc(wj.getWjmc());
                    zwjDto.setLx(WjlxEnum.WJ.getCode());
                    list.add(zwjDto);
                }
            }
        }
    }
}
